using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JGSY.CMS.LowCode.Platform.Application.Interfaces;
using JGSY.CMS.LowCode.Platform.Infrastructure.CmsDbContext;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;

namespace JGSY.CMS.LowCode.Platform.Infrastructure.Services
{
    /// <summary>
    /// 用户有效权限服务
    /// 聚合用户角色和角色权限，提供高性能的权限判断和缓存管理功能
    /// </summary>
    /// <remarks>
    /// 该服务的核心功能：
    /// - 通过用户-角色-权限关联链路聚合用户的有效权限
    /// - 使用内存缓存提升权限查询性能
    /// - 支持权限缓存的手动失效和自动过期
    /// - 提供权限判断的快速查询接口
    /// 
    /// 缓存策略：
    /// - 绝对过期时间：10分钟
    /// - 滑动过期时间：5分钟
    /// - 缓存键格式：user:perms:{userId}
    /// </remarks>
    public class UserPermissionService : IUserPermissionService
    {
        private readonly LowCodeDbContext _db;
        private readonly IMemoryCache _cache;
        private readonly ILogger<UserPermissionService> _logger;

        private static readonly string CachePrefix = "user:perms:";
        private readonly MemoryCacheEntryOptions _cacheOptions;

        /// <summary>
        /// 初始化用户权限服务
        /// </summary>
        /// <param name="db">数据库上下文</param>
        /// <param name="cache">内存缓存</param>
        /// <param name="logger">日志记录器</param>
        /// <exception cref="ArgumentNullException">当任何参数为null时抛出</exception>
        public UserPermissionService(
            LowCodeDbContext db,
            IMemoryCache cache,
            ILogger<UserPermissionService> logger)
        {
            _db = db ?? throw new ArgumentNullException(nameof(db));
            _cache = cache ?? throw new ArgumentNullException(nameof(cache));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            
            _cacheOptions = new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10),
                SlidingExpiration = TimeSpan.FromMinutes(5)
            };
        }

        /// <summary>
        /// 使指定用户的权限缓存失效
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <remarks>
        /// 在用户角色发生变更或权限配置更新时调用，
        /// 确保下次权限查询时重新从数据库加载最新权限
        /// </remarks>
        public void InvalidateCache(long userId)
        {
            var cacheKey = CachePrefix + userId;
            _cache.Remove(cacheKey);
            _logger.LogInformation("已清除用户 {UserId} 的权限缓存", userId);
        }

        /// <summary>
        /// 获取用户的有效权限集合
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户拥有的权限代码集合</returns>
        /// <exception cref="Exception">数据库查询异常</exception>
        /// <remarks>
        /// 权限获取流程：
        /// 1. 首先尝试从缓存获取权限
        /// 2. 如果缓存未命中，查询数据库
        /// 3. 通过用户角色关联查询所有角色权限
        /// 4. 去重并缓存结果
        /// 
        /// 性能优化：
        /// - 使用HashSet确保权限唯一性和快速查找
        /// - 内存缓存减少数据库查询
        /// - 支持异步操作和取消
        /// </remarks>
        public async Task<HashSet<string>> GetEffectivePermissionsAsync(long userId, CancellationToken cancellationToken = default)
        {
            var cacheKey = CachePrefix + userId;
            if (_cache.TryGetValue(cacheKey, out HashSet<string>? cached) && cached is not null)
            {
                return cached;
            }

            // 加载用户的角色 -> 角色权限 -> 权限Code
            // 需要确保导航关系已配置；这里直接查询联接以减少跟踪与N+1。
            var codes = await _db.UserRoles
                .AsNoTracking()
                .Where(ur => ur.UserId == userId)
                .Join(_db.RolePermissions.AsNoTracking(), ur => ur.RoleId, rp => rp.RoleId, (ur, rp) => rp)
                .Join(_db.Permissions.AsNoTracking(), rp => rp.PermissionId, p => p.Id, (rp, p) => p.Code)
                .Where(code => code != null && code != "")
                .Select(code => code!)
                .ToListAsync(cancellationToken);

            var set = codes.Count == 0
                ? new HashSet<string>(StringComparer.OrdinalIgnoreCase)
                : new HashSet<string>(codes, StringComparer.OrdinalIgnoreCase);

            _cache.Set(cacheKey, set, _cacheOptions);
            _logger.LogDebug("Loaded {Count} permissions for user {UserId}", set.Count, userId);
            return set;
        }

        /// <summary>
        /// 检查用户是否拥有指定权限
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <param name="permissionCode">权限代码</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户拥有指定权限时返回true，否则返回false</returns>
        /// <remarks>
        /// 权限检查策略：
        /// - 权限代码为空或空白时默认允许（宽松策略）
        /// - 使用有效权限缓存进行快速查找
        /// - 权限代码比较忽略大小写
        /// 
        /// 性能特征：
        /// - 第一次查询会触发权限加载和缓存
        /// - 后续查询直接使用缓存，响应时间极快
        /// - 支持异步操作，不阻塞调用线程
        /// </remarks>
        public async Task<bool> HasPermissionAsync(long userId, string permissionCode, CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrWhiteSpace(permissionCode)) return true; // 空策略视为允许
            var perms = await GetEffectivePermissionsAsync(userId, cancellationToken);
            var allowed = perms.Contains(permissionCode);
            _logger.LogDebug("Permission check user={UserId} code={Code} => {Allowed}", userId, permissionCode, allowed);
            return allowed;
        }
    }
}
